/*
 *  Copyright 2010 UPF Telecos 4th group G.
 * 
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 * 
 *       http://www.apache.org/licenses/LICENSE-2.0
 * 
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *  under the License.
 */
package edu.upf.database.impl;

import edu.upf.content.DescriptionContent;
import edu.upf.content.ImageObject;
import edu.upf.content.ImageHandler;
import edu.upf.content.Video;
import edu.upf.content.VideoHandler;
import edu.upf.database.DBAction;
import edu.upf.database.DBActionResult;
import edu.upf.database.DBManager;
import edu.upf.database.XmlDbDataSource;
import edu.upf.utils.Definitions;
import edu.upf.utils.LogSystem;
import edu.upf.exceptions.DBConnectionFailed;
import edu.upf.utils.NetInfo;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import org.xmldb.api.base.Collection;
import org.xmldb.api.base.Resource;
import org.xmldb.api.base.ResourceIterator;
import org.xmldb.api.base.ResourceSet;
import org.xmldb.api.base.Service;
import org.xmldb.api.base.XMLDBException;
import org.xmldb.api.modules.CollectionManagementService;
import org.xmldb.api.modules.XMLResource;
import org.xmldb.api.modules.XQueryService;
import static java.text.MessageFormat.format;


/**
 * Implements the database managing and command execution
 * @author netjois
 */
public final class DBManagerImpl implements DBManager, Definitions {

    private static DBManager manager;
    private XmlDbDataSource dataSource;
    private Collection collection;
    private String currentAction;
    private Object[] parameters;
    private Service service;
    private DBActionResult result;
    private Properties properties;
    private LogSystem log;
    private ResourceSet resultSet;
    private List queryResults;


    /**
     * Constructor, initializes properties and log.
     */
    private DBManagerImpl() throws DBConnectionFailed, IOException{
        log = new LogSystem(DBManagerImpl.class);
        properties = new Properties();
        properties.load(getClass().getResourceAsStream("/DB.properties"));
    }

    /**
     * Returns the unique instance, or creates it if it does not exist.
     * @return
     * @throws DBConnectionFailed
     * @throws IOException 
     */
    public static synchronized DBManager getInstance() throws DBConnectionFailed, IOException{
        if (manager == null) {
            manager = new DBManagerImpl();
        }
        return manager;
    }

    @Override
    public DBActionResult execute(DBAction aDBAction) {

        dataSource = EXistDataSource.getInstance();
        result = new DBActionResultImpl();

        try {
            String password = properties.getProperty("XmlDbDatasource/password");
            collection = dataSource.getCollection(DBUSER,password );
        } catch (XMLDBException ex) {
            log.addInfoLog(ex.getMessage());
            setExceptionResult(ex);
        }
        if(collection!=null){
            currentAction=aDBAction.getActionId();
            parameters=aDBAction.getParameters();

            if (currentAction.matches(QUERY_ACTION)) {
                executeQuery();
            } else if (currentAction.matches(ADD_ACTION)) {
                executeAdd();
            } else if (currentAction.matches(UPDATE_ACTION)) {
                executeUpdate();
            } else if(currentAction.matches(RETRIEVE_ACTION)){
                executeRetrieve(currentAction);
            } else{
                setExceptionResult(new DBConnectionFailed("Unrecognized DBAction"));
            }
        }else{
            setExceptionResult(new DBConnectionFailed("Database access falied"));
        }
        EXistDataSource.returnInstance(dataSource);
        return result;
    }

    private void executeQuery() {
        try {
            service = collection.getService(XQUERY_SERVICE, XQUERY_VERSION);
            String search = format(properties.getProperty(currentAction),parameters);
            resultSet = ((XQueryService)service).query(search);
            ResourceIterator iterator = resultSet.getIterator();
            if(search.matches(IMAGE_RESULT)){
                setImageResults(iterator);
            }else if(search.matches(VIDEO_RESULT)){
                setVideoResults(iterator);
            }
        } catch (XMLDBException ex) {
            setExceptionResult(ex);
            log.addInfoLog(ex.getCause()+ex.getMessage());
        }
    }

    private void executeRetrieve(String action){
        executeQuery();
        if(action.matches(IMAGE_RESULT)){
            Iterator<DescriptionContent> i=queryResults.iterator();
            DescriptionContent currentContent;
            while(i.hasNext()){
                currentContent = i.next();
                ((ImageObject)currentContent).createThumbnail(LIB_THUMB_H, LIB_THUMB_W);
            }
        }
        if(action.matches(VIDEO_RESULT)){
            Iterator<DescriptionContent> i=queryResults.iterator();
            DescriptionContent currentContent;
            URL defaultThumb=getClass().getResource("/thumb_icons/video_thumb.png");
            while(i.hasNext()){
                currentContent = i.next();
                ((Video)currentContent).createThumbnail(defaultThumb, LIB_THUMB_H, LIB_THUMB_W);
            }
        }
        
    }

    private void executeAdd() {
        try {
            // creates a new resource XMLResource
            File file = new File((String)parameters[DOCUMENT_INDEX]);
            XMLResource document =(XMLResource)collection.
                    createResource(file.getName(), "XMLResource");
            document.setContent(file);

            String collectionPath = (String) parameters[COLLECTION_INDEX];
            service = collection.getService(ADD_SERVICE, ADD_VERSION);
            collection = ((CollectionManagementService)service).
                    createCollection(collectionPath.substring("/db/".length()));

            log.addInfoLog("storing document " + document.getId() + "...");
            collection.storeResource(document);
            List resultList = new ArrayList();
            resultList.add(document.getId());
            result.setResult(resultList);

        } catch (XMLDBException ex) {
                                    log.addInfoLog("add"+ex.getCause()+ex.getMessage());
            setExceptionResult(ex);
        }
    }

    private void executeUpdate(){
        try {
            service = collection.getService(UPDATE_SERVICE, UPDATE_VERSION);
            String search = format(properties.getProperty(currentAction),parameters);
            resultSet = ((XQueryService)service).query(search);

            ResourceIterator i = resultSet.getIterator();
            queryResults = new ArrayList();
            while(i.hasMoreResources()) {
                Resource r = i.nextResource();
                queryResults.add((String)r.getContent());
            }
            result.setResult(queryResults);

        } catch (XMLDBException ex) {
                        log.addInfoLog("update"+ex.getCause()+ex.getMessage());
            setExceptionResult(ex);
        }
    }

    private void setExceptionResult(Exception ex){
        result.setException(ex);
        result.setErrorMsg(ex.getMessage());
    }

    private void setImageResults(ResourceIterator i) throws XMLDBException {      
        queryResults = new ArrayList();
        while (i.hasMoreResources()) {
            XMLResource resource = (XMLResource) i.nextResource();
            ImageObject image = new ImageObject();
            ImageHandler handler = new ImageHandler(image);
            resource.getContentAsSAX(handler);
            image.createThumbnail(THUMBHEIGHT,THUMBWIDTH);
            queryResults.add(image);
        }
        result.setResult(queryResults);
    }

    private void setVideoResults(ResourceIterator i) throws XMLDBException {
        queryResults = new ArrayList();
        String ip=NetInfo.getInstance().getIP();
        while (i.hasMoreResources()) {
            XMLResource resource = (XMLResource) i.nextResource();
            Video video = new Video();
            VideoHandler handler = new VideoHandler(video);
            resource.getContentAsSAX(handler);
            video.setIP(ip);
            queryResults.add(video);
        }
        result.setResult(queryResults);
    }
}
